package com.llbnk.springcoding.thread;

import com.alibaba.fastjson2.JSON;
import com.ctrip.framework.apollo.ConfigService;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.llbnk.springcoding.thread.rejectedPolicy.RejectedPolicyEnum;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;
import java.util.concurrent.*;

import static com.llbnk.springcoding.thread.ThreadEnum.ThreadExcutorError.*;


@Slf4j
public class ThreadExcutorFactory {

    private static ConcurrentHashMap<String,ThreadExcutorWrapper> threadExcutorMap = new ConcurrentHashMap<>(128);



    public static ThreadExcutorWrapper build(String poolName, int core, int max,
                                             long keepAliveTime,
                                             int capacity, boolean allowCoreThreadTimeOut,
                                             boolean waitForTasksToComplete,
                                             long awaitTerminationSeconds,
                                             RejectedPolicyEnum rejectedPolicyEnum) throws ThreadExcutorException {
        ThreadExcutorConfig defaultCfg = new ThreadExcutorConfig();
        defaultCfg.setCore(core);
        defaultCfg.setMax(max);
        defaultCfg.setKeepAliveTime(keepAliveTime);
        defaultCfg.setCapacity(capacity);
        defaultCfg.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
        defaultCfg.setAwaitTerminationSeconds(awaitTerminationSeconds);
        defaultCfg.setWaitForTasksToComplete(waitForTasksToComplete);
        ThreadExcutorConfig cfg = ConfigService.getAppConfig()
                .getProperty(ThreadEnum.ThreadExcutorName.THREAD_POOL_NAME_PREFIX.getValue() + poolName,
                        (val) -> JSON.parseObject(val, ThreadExcutorConfig.class), null);
        log.info("pool name:{}, apollo cfg:{}", poolName, JSON.toJSONString(cfg));
        return build(poolName, cfg, defaultCfg, rejectedPolicyEnum);
    }


    private static ThreadExcutorWrapper build(String poolName, ThreadExcutorConfig cfg, ThreadExcutorConfig defaultCfg, RejectedPolicyEnum rejectedPolicyEnum) throws ThreadExcutorException {
        //1.判断线程池名称
        if(Objects.isNull(poolName)){
            //add log or monitor
            log.error("pool name:{},build fail ,because name is invalid",poolName);
            throw new ThreadExcutorException(POOL_NAME_IS_INVALID.getValue(), POOL_NAME_IS_INVALID.getCode());
        }
        //2.判断线程池参数是否合法
        if(Objects.isNull(defaultCfg) || !defaultCfg.isValid() || (!Objects.isNull(cfg) && !cfg.isValid())){
            //add log or monitor
            log.error("pool name:{},build fail ,because params is illegal,defaultCfg:{},cfg:{}",poolName,defaultCfg,cfg);
            throw new ThreadExcutorException(POOL_PARAMS_IS_ILLEGAL.getValue(), POOL_PARAMS_IS_ILLEGAL.getCode());
        }
        //3.是否是重复线程池
        if(!Objects.isNull(threadExcutorMap.getOrDefault(poolName,null))){
            //add log or monitor
            log.error("pool name:{},build fail ,because poolName is repeated,defaultCfg:{},cfg:{}",poolName,defaultCfg,cfg);
            throw new ThreadExcutorException(POOL_IS_REPEATED.getValue(), POOL_IS_REPEATED.getCode());
        }
        //4.加对象锁(加锁直接用最底层字符串池中的字符对象)
        synchronized (poolName.intern()){
            //5.再次判断
            if(!Objects.isNull(threadExcutorMap.getOrDefault(poolName,null))){
                //add log or monitor
                log.error("pool name:{},build fail ,because poolName is repeated,defaultCfg:{},cfg:{}",poolName,defaultCfg,cfg);
                throw new ThreadExcutorException(POOL_IS_REPEATED.getValue(), POOL_IS_REPEATED.getCode());
            }
            //6.设置默认线程池参数
            if(Objects.isNull(cfg)){
                cfg = defaultCfg;
            }
            if(Objects.isNull(rejectedPolicyEnum)){
                rejectedPolicyEnum = RejectedPolicyEnum.ABORT_POLICY;
            }
            //7.创建线程池并放入map中
            log.info("pool name:{},params cfg:{}",poolName,cfg);
            //使用ExcutorService实现类
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(cfg.getCore(), cfg.getMax(), cfg.getKeepAliveTime(),
                    TimeUnit.SECONDS, new ChangeCapacityBlockingQueue<>(cfg.getCapacity()),
                    new ThreadFactoryBuilder().setNameFormat(poolName + "-%d").build(),
                    rejectedPolicyEnum.getRejectPolicy(poolName));

            ThreadExcutorWrapper threadExcutorWrapper = new ThreadExcutorWrapper(cfg, poolName, threadPoolExecutor);
            threadExcutorMap.put(poolName,threadExcutorWrapper);
            return threadExcutorWrapper;
        }


    }

    public static ThreadExcutorWrapper getThreadExcutor(String poolName){
        return threadExcutorMap.get(poolName);
    }
}
